#include <stdio.h>
#include <stdlib.h>
#include "wled.h"

static inline void analysis(char buf[], int &request_type, int &request, int &value, int &index, int &length)
{
	request_type = buf[0];
	request = buf[1];
	value = buf[2]*256 + buf[3];
	index = buf[4]*256 + buf[5];
	length = buf[6]*256 + buf[7];	
}

static inline void analysis(char buf[], int &request_type, int &request, int &value, int &index, int &length, char *&data)
{
	request_type = buf[0];
	request = buf[1];
	value = buf[2]*256 + buf[3];
	index = buf[4]*256 + buf[5];
	length = buf[6]*256 + buf[7];	
	data = buf + 8;
}

/* is it the 'turn on' urbs? */
static int isturnon(char buf1[], char buf2[], int &light)
{
	int request_type, request, value, index, length;
	analysis(buf1, request_type, request, value, index, length);
	if(request_type==0x48 && request==12 && value==0x140a && (index==4 || index==2 || index==1) && length==0 )
	  {
		light = index;
		analysis(buf2, request_type, request, value, index, length);
		if(request_type==0x48 && request==12 && value==0x0c0a && index==light && length==0 )
			return 1;
	  }
	/* is not */
	return 0;
}

static int isturnoff(char buf1[], char buf2[], int &light)
{
	int request_type, request, value, index, length;
	analysis(buf1, request_type, request, value, index, length);
	if(request_type==0x48 && request==12 && value==0x140a && (index==4 || index==2 || index==1) && length==0 )
	  {
		light = index;
		analysis(buf2, request_type, request, value, index, length);
		index >>= 8;
		if(request_type==0x48 && request==12 && value==0x0c0a && index==light && length==0 )
			return 1;
	  }
	/* is not */
	return 0;	
}

static int isflash(char buf1[], char buf2[], char buf3[], int &light)
{
	if(isturnoff(buf1, buf2, light))
	  {
		int request_type, request, value, index, length;
		analysis(buf3, request_type, request, value, index, length);
		index >>= 8;
		if(request_type==0x48 && request==12 && value==0x140a && index==light && length==0 )
			return 1;
	  }
	return 0;
}

static int isfreq(char buf[], int &freq)
{
	int request_type, request, value, index, length;
	analysis(buf, request_type, request, value, index, length);
	if(request_type==0x48 && request==12 && value==0x130a && index<256 && length==0 )
		return freq = index, 1;
	return 0;
}

static int isontime(char buf[], int &color, int &ontime)
{
	int request_type, request, value, index, length;
	analysis(buf, request_type, request, value, index, length);
	if(request_type==0x48 && request==12 && (value==0x170a||value==0x160a||value==0x150a)
	  && (index&0xFF)==0x64 && length==0 )
		return color=(value>>8)&0xFF, ontime=(index>>8)&0xFF, 1;
	return 0;
}

static int isofftime(char buf[], int &color, int &offtime)
{
	int request_type, request, value, index, length;
	analysis(buf, request_type, request, value, index, length);
	if(request_type==0x48 && request==12 && (value==0x170a||value==0x160a||value==0x150a)
	  && (index&0xFF00)==0x6400 && length==0 )
		return color=(value>>8)&0xFF, offtime=index&0xFF, 1;
	return 0;
}

static int islightness(char buf[], int &color, int &lightness)
{
	int request_type, request, value, index, length;
	analysis(buf, request_type, request, value, index, length);
	color=index&0xFF;
	if(request_type==0x48 && request==12 && value==0x220a && color<3 && length==0)
		return lightness=(index>>8)&0xFF, 1;
	return 0;
}

static int isloudspeaker(char buf1[], char buf2[], int &freq, int &num)
{
	int request_type, request, value, index, length;
	char *data;
	analysis(buf1, request_type, request, value, index, length, data);
	freq=(index>>8)&0xFF;
	num=(unsigned char)data[0];
	if(request_type==8 && request==12 && value==0x460a && (index&0xFF)==1 && length==8
		&& data[1]==1 && data[2]==1 && !data[3] && !data[4] 
		&& !data[5] && !data[6] && !data[7])
		  {
			  analysis(buf2, request_type, request, value, index, length);
			  if(request_type==0x48 && request==12 && value==0x460a && index==(1|(freq<<8)) && length==0)
				return 1;
		  }
	return 0;
}

void ReadURB::copewith(char buf[])
{
	int request_type, request, value, index, length;
	analysis(buf, request_type, request, value, index, length);
	
	/* debug message */
	static int n=0;
	printf("urb %d\n", ++n);

	emit showurb( request_type, request, value, index, length, length ? buf+8 : NULL );
	
	int x[] = {0, WLed::GREEN, WLed::RED, 0, WLed::BLUE};
	int color, freq, ontime, offtime, lightness, num;
	if ( isturnon(this->hist[0], buf, color) )
		emit setstate(x[color], WLed::ON);
	else if ( isturnoff(this->hist[0], buf, color) )
		emit setstate(x[color], WLed::OFF);
	else if ( isflash(this->hist[1], this->hist[0], buf, color) )
		emit setstate(x[color], WLed::FLASH);
	else if ( isfreq(buf, freq) )
	  {
		emit setfreq(WLed::RED, freq);
		emit setfreq(WLed::GREEN, freq);
		emit setfreq(WLed::BLUE, freq);
	  }
	else if ( isontime(buf, color, ontime) )
		emit setontime(color==0x17?WLed::BLUE:(color==0x16?WLed::RED:WLed::GREEN), ontime);
	else if ( isofftime(buf, color, offtime) )
		emit setofftime(color==0x17?WLed::BLUE:(color==0x16?WLed::RED:WLed::GREEN), offtime);
	else if ( islightness(buf, color, lightness) )
		emit setlightness(color==2 ? WLed::BLUE : (color==1 ? WLed::RED : WLed::GREEN), lightness);
	else if ( isloudspeaker(this->hist[0], buf, freq, num) )
		emit loudspeaker(freq, num);
}

void ReadURB::run()
{
	char buffer[16];
	FILE *f;
	static int x;
	
	if(!(f = fopen("/proc/led","r"))) {
		perror("/proc/led");
		exit(1);
	}
	
	while( x++ < this->maxurb || this->maxurb==0 ){
		int ret;
		if((ret = fread(buffer, 16, 1, f))!=1) {
			printf("%d\n",ret);
			perror("fread");
			break;
		}
		this->copewith(buffer);
		memcpy(this->hist[2], this->hist[1], 16);
		memcpy(this->hist[1], this->hist[0], 16);
		memcpy(this->hist[0], buffer, 16);
		fflush(f); // clean buffer
		fseek(f,0,SEEK_SET);
	}
	
	printf("sub thread 'readurb' terminates.\n");
	QApplication::exit(1);
}

ReadURB::ReadURB(WLed *window, int maxurb, QObject* parent): QThread(parent)
{
	this->window = window;
	this->maxurb = maxurb;
	memset(this->hist, 0, sizeof this->hist);

	connect(this, SIGNAL(setstate(int,int)), this->window, SLOT(setstate(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(setlightness(int,int)), this->window, SLOT(setlightness(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(setfreq(int,int)), this->window, SLOT(setfreq(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(setontime(int,int)), this->window, SLOT(setontime(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(setofftime(int,int)), this->window, SLOT(setofftime(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(loudspeaker(int,int)), this->window, SLOT(loudspeaker(int,int)), Qt::DirectConnection);
	connect(this, SIGNAL(showurb(int,int,int,int,int,char*)), this->window, SLOT(showurb(int,int,int,int,int,char*)), Qt::DirectConnection);
	
	emit setstate(WLed::RED, WLed::OFF);
	emit setstate(WLed::GREEN, WLed::OFF);
	emit setstate(WLed::BLUE, WLed::OFF);
}
